﻿using System;
using System.Collections.Generic;
using Reactor.Messages.Events.Endpoint;

namespace Reactor.Messaging.ServiceBus
{
    /// <summary>
    /// Interface defining the operations and capabilities of the Reactor Service Bus.
    /// </summary>
    public interface IServiceBus : IDisposable
    {
        #region Events

        /// <summary>
        /// Occurs when a message is sent.
        /// </summary>
        event EventHandler<MessageEventArgs> MessageSent;

        /// <summary>
        /// Occurs when a message is received.
        /// </summary>
        event EventHandler<MessageEventArgs> MessageReceived;

        /// <summary>
        /// Occurs when the service bus run status has changed.
        /// </summary>
        event EventHandler<RunStatusChangedEventArgs> RunStatusChanged;

        /// <summary>
        /// Occurs when subscriptions are updated. That is, whenever a 
        /// subscription is created or terminated.
        /// </summary>
        event EventHandler<SubscriptionUpdateEventArgs> SubscriptionsUpdated;

        #endregion

        /// <summary>
        /// Gets or sets the configuration instance this bus is to use.
        /// </summary>
        /// <value>The configuration.</value>
        BusConfiguration Configuration { get; set; }

        /// <summary>
        /// Gets or sets a value indicating whether this instance is running.
        /// </summary>
        /// <value>
        /// 	<c>true</c> if this instance is running; otherwise, <c>false</c>.
        /// </value>
        bool IsRunning { get; }

        /// <summary>
        /// Starts the bus by opening a connection to the underlying broker(s). If connection fails, a <seealso cref="Reactor.Messaging.Exceptions.ConnectionException"/> is thrown.
        /// </summary>
        void Start();

        /// <summary>
        /// Stops the service bus by taking the current instance offline.
        /// </summary>
        void Stop();

        /// <summary>
        /// Sends the specified message. The service bus will first lookup all registered destinations for the message type 
        /// using the <see cref="IDestinationRegistry"/> isntance configured.
        /// </summary>
        /// <typeparam name="T">The message type</typeparam>
        /// <param name="message">The message to send.</param>
        void Send<T>(T message) where T : class;

        /// <summary>
        /// Sends the specified message. The service bus will first lookup all registered destinations for the message type 
        /// using the <see cref="IDestinationRegistry"/> isntance configured.
        /// </summary>
        /// <typeparam name="T">The message type</typeparam>
        /// <param name="message">The message to send.</param>
        /// <param name="workflowId">The workflow id of the workflow this message is participating in.</param>
        void Send<T>(T message, Guid workflowId) where T : class;

        /// <summary>
        /// Sends the specified message to the specified MessagingChannel.
        /// </summary>
        /// <typeparam name="T">The type of message to send.</typeparam>
        /// <param name="message">The message to send. If the message is null, an ArgumentNullException is thrown.</param>
        /// <param name="messagingChannel">The MessagingChannel. If the MessagingChannel is null, an ArgumentNullException is thrown.</param>
        void Send<T>(T message, IMessagingChannel messagingChannel) where T : class;

        /// <summary>
        /// Sends the specified message to the specified MessagingChannel.
        /// </summary>
        /// <typeparam name="T">The type of message to send.</typeparam>
        /// <param name="message">The message to send. If the message is null, an ArgumentNullException is thrown.</param>
        /// <param name="messagingChannel">The MessagingChannel. If the MessagingChannel is null, an ArgumentNullException is thrown.</param>
        /// <param name="workflowId">The workflow id of the workflow this message is participating in.</param>
        void Send<T>(T message, IMessagingChannel messagingChannel, Guid workflowId) where T : class;

        /// <summary>
        /// Sends a message designated as a request. Receivers of request messages should reply to the
        /// MessagingChannel provided to this method. This overload will send the request only to the destinations
        /// provided to this method instead of sending to anyone with an interest in the supplied message type.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="message">The message.</param>
        /// <param name="destination">The receiving MessagingChannel.</param>
        /// <param name="replyDestination">The reply MessagingChannel.</param>
        /// <returns>
        /// An identifer uniquely identifying the request/reply exchange of message.
        /// </returns>
        Guid Request<T>(T message, IEnumerable<IMessagingChannel> destination, IMessagingChannel replyDestination) where T : class;

        /// <summary>
        /// Sends a message designated as a request. Receivers of request messages should reply to the
        /// MessagingChannel provided to this method. This overload will send the request to anybody registered 
        /// with an interest in this message type.
        /// </summary>
        /// <typeparam name="T">The message type.</typeparam>
        /// <param name="message">The message.</param>
        /// <param name="replyDestination">The reply MessagingChannel.</param>
        /// <returns>An identifer uniquely identifying the request/reply exchange of message.</returns>
        Guid Request<T>(T message, IMessagingChannel replyDestination) where T : class;

        /// <summary>
        /// Sends a message designated as a request. Receivers of request messages should reply to the 
        /// MessagingChannel provided to them. When using this overload, the reply MessagingChannel is the current 
        /// endpoint's input queue. The request is sent to all parties registered with an interest in 
        /// the supplied message type.
        /// </summary>
        /// <typeparam name="T">The message type.</typeparam>
        /// <param name="message">The message.</param>
        /// <returns>An identifer uniquely identifying the request/reply exchange of message.</returns>
        Guid Request<T>(T message) where T : class;

        /// <summary>
        /// Creates a subscription based on the type of message expected
        /// and the expected MessagingChannel. This overload sends a single 
        /// <seealso cref="EndpointSubscriptionChanged"/> message once all subscriptions are created.
        /// </summary>
        /// <typeparam name="T">The message handler type.</typeparam>
        /// <param name="expectedMessagingChannel">The expected MessagingChannel.</param>
        void Subscribe<T>(IMessagingChannel expectedMessagingChannel) where T : class;

        /// <summary>
        /// Creates multiple subscriptions based on the type of message expected and provided 
        /// destinations. This overload sends only a single <seealso cref="EndpointSubscriptionChanged"/> 
        /// message once all subscriptions are created.
        /// </summary>
        /// <typeparam name="T"></typeparam>
        /// <param name="expectedDestinations">The expected destinations.</param>
        void Subscribe<T>(IEnumerable<IMessagingChannel> expectedDestinations) where T : class;

        /// <summary>
        /// Creates a new subscription based on the type of message expected. This overload assumes 
        /// the expected MessagingChannel is a PubSubChannel with the same name as the message type and 
        /// cascades into the overload that accepts a single IMessagingChannel.
        /// </summary>
        /// <typeparam name="T">The message type.</typeparam>
        void Subscribe<T>() where T : class;

        /// <summary>
        /// Deletes subscriptions matching the type of message expected and specified MessagingChannel.
        /// </summary>
        /// <typeparam name="T">The message type.</typeparam>
        /// <param name="expectedMessagingChannel">The expected MessagingChannel.</param>
        void UnSubscribe<T>(IMessagingChannel expectedMessagingChannel) where T : class;

        /// <summary>
        /// Resets the message sent count and returns the number of messages sent since last time 
        /// this method was called on the instance.
        /// </summary>
        /// <returns></returns>
        ulong ResetMessageSentCount();

        /// <summary>
        /// Resets the message received count and returns the number of messages sent since last time 
        /// this method was called on the instance.
        /// </summary>
        /// <returns></returns>
        ulong ResetMessageReceivedCount();

        /// <summary>
        /// Creates multiple subscriptions based on the type of message expected and provided
        /// destinations. This overload sends only a single <seealso cref="EndpointSubscriptionChanged"/>
        /// message once all subscriptions are created.
        /// </summary>
        /// <param name="messageType">Type of the message.</param>
        /// <param name="expectedDestinations">The expected destinations.</param>
        void Subscribe(Type messageType, IEnumerable<IMessagingChannel> expectedDestinations);
    }
}
